#include "ProcesoJugador.h"
#include "Carta.h"
#include "Fifo.h"
#include "Aviso.h"
#include "Canales.h"
#include "Logger.h"
#include "FuncionesAuxiliares.h" // avisar, recibirAviso
#include <cstring>
#include <sys/wait.h>
#include <iostream> //cout

#define CANTCARTAS 4
#define TAMBUFFER 100
#define PRIMERJUGADOR 1
#define DEBUG "debug"

using namespace std;

ProcesoJugador::ProcesoJugador(char* unJugador, char* totalJugadores, char *debug) : Receptor(), Trasmisor() {	
	this->jugadorNombre = string(unJugador);
	this->totalJugadores = string(totalJugadores);
	this->jugador = Jugador();
	
	if (strcmp(debug, DEBUG) == 0) {
		Logger::activarDebug();
	}
	
	// Creo el proceso Recibidor.
	int pidRecibidor = fork();
	if (pidRecibidor == 0) { 
		execlp((char*) "./procRecibidor", (char*) "procRecibidor", unJugador, debug, (char*) NULL);
	}
	
	// Creo el proceso Perdedor.
	int pidPerdedor = fork();
	if (pidPerdedor == 0) { 
		execlp((char*) "./procPerdedor", (char*) "procPerdedor", unJugador, debug, (char*) NULL);
	}
	
}


ProcesoJugador::~ProcesoJugador() {
}


int ProcesoJugador::correr() {
	
	string remitente = "JUGADOR " + this->jugadorNombre;
	
	abrirCanalesCoordinador();	
	
	abrirCanalLecturaCarta();
	abrirCanalEscrituraCarta();
	
	recibirOKRecibidor();
	avisar(aCoord, OK);
	Logger::loggear("Avisé al coordinador del inicio",remitente);
	Aviso seguirJuego = recibirAviso(deCoord);
	
	while ( seguirJuego.getTipo() == OK ) {
		Logger::loggear("Voy a recibir cartas",remitente);	
		recibirCartasIniciales();
		
		Logger::loggear("Espero aviso del coord para arrancar RONDA",remitente);
		
		// Espero aviso para arrancar ronda.
		Aviso aviso = recibirAviso(deCoord);
		int rondaTerminada = 0;
			
		while( ! rondaTerminada ) {
			pasarCarta();
			recibirCarta();
			
			Logger::loggear("Juego mano",remitente);
			
			if (jugador.gano()) {
				avisar(aCoord, GANO);
				cout << "JUGADOR "<< this->jugadorNombre << " ganó." << endl;
				Logger::loggear("GANEEE!!!",remitente);
			} else {
				avisar(aCoord, OK);	
			}
			aviso = recibirAviso(deCoord);
			if (aviso.getTipo() != OK)
				rondaTerminada = 1;
		}
		
		jugador.reiniciarRonda();
		Logger::loggear("Voy a recibirAviso de seguirJuego",remitente);
		seguirJuego = recibirAviso(deCoord);
	}
	
	matarRecibidorSiguiente(); 
	cerrarCanalLectura();
	Logger::loggear("Voy a hacer los wait",remitente);

	wait(NULL);	// procRecibidor
	wait(NULL); // procPerdedor
	
	cerrarCanalEscritura();
	cerrarCanalesCoordinador();
	Logger::loggear("Finaliza",remitente);

	return 0;
}


void ProcesoJugador::abrirCanalLecturaCarta() {
	string archivo = string(RECIBIDORJUGADOR) + this->jugadorNombre;
	Receptor::abrirCanalLectura((char*) archivo.c_str());
}


void ProcesoJugador::abrirCanalEscrituraCarta() {
	string valor = this->jugadorNombre;
	
	// El jugador debe "conectarse" con el siguiente.
	// Si es el ultimo, debe conectarse con el primero
	if (jugadorNombre.compare(totalJugadores) != 0)
		valor = incrementarJugador();
	else
		valor = itos(PRIMERJUGADOR);
	string archivo = string(JUGADORRECIBIDOR) + valor;
	Trasmisor::abrirCanalEscritura((char*) archivo.c_str());
}


void ProcesoJugador::abrirCanalesCoordinador() {
	string nombreACoord = string(JUGADORCOORD) + jugadorNombre;
	aCoord = new Fifo( nombreACoord.c_str() );
	string nombreDeCoord = string(COORDJUGADOR) + jugadorNombre;
	deCoord = new Fifo( nombreDeCoord.c_str() );
}


void ProcesoJugador::cerrarCanalesCoordinador() {
	deCoord->cerrar();
	aCoord->cerrar();
	aCoord->eliminar();
}


void ProcesoJugador::matarRecibidorSiguiente() {
	avisar(escritor, FINDELJUEGO);
}	


void ProcesoJugador::pasarCarta() {
	int tamBuff;
	Carta cartaAPasar = jugador.dejarCarta();
	char* buffCarta = cartaAPasar.serializar(&tamBuff);
	escribir(buffCarta, tamBuff);
	delete[] buffCarta;
}


string ProcesoJugador::incrementarJugador() {
	int valor = atoi((this->jugadorNombre).c_str()) + 1;
	return itos(valor);
}


void ProcesoJugador::recibirCarta() {
	leer();
	Carta cartaNueva = Carta::deserializar(buffer, bytesLeidos);
	jugador.tomarCarta(cartaNueva);
}	


void ProcesoJugador::recibirCartasIniciales() {
	for (int i = 0; i < CANTCARTAS; i++) {
		char buffer[TAMBUFFER];
		int leido = deCoord->leer(buffer, TAMBUFFER);
		Carta carta = Carta::deserializar(buffer, leido);
		jugador.tomarCarta(carta);
		// Le mando un ACK por cada carta recibida
		avisar(aCoord, OK);
	}
}


void ProcesoJugador::recibirOKRecibidor() {
	leer();
}
